package org.sdnstproject.interlayer.interlayerProxy;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;

import org.projectfloodlight.openflow.protocol.OFControllerMaxLen;
import org.projectfloodlight.openflow.protocol.OFType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class OFFuzzer {
	private static final Logger log = LoggerFactory.getLogger(OFFuzzer.class);
	private Configuration config = Configuration.getInstance();
	private AgentManager manager;
	public volatile boolean running = true;
	private ResultAnalyzer analyzer = ResultAnalyzer.getInstance();
	private DummySwitch ofSwitch;
	private DummyController ofController;
	
	public OFFuzzer() {
        this.manager = new AgentManager();
    }
	
	public String generateFlow(String proto) throws Exception{
        manager.hostmWriter(proto);
        String result = manager.hostmReader();
        if(result == null) log.error("generate flow and get result failed.");
        return result;
    }
	
	public void doMoreFuzzer() {
		String[] stras = new String[] {
			"ofVersion:1.3,ofType:FEATURES_REPLY,testWhich:controller,action:MODIFY,field:reserved,actionValue:8882949672954",
			"ofVersion:1.3,ofType:PORT_STATUS,testWhich:controller,action:MODIFY,field:supported,actionValue:4294967295"
		};
		FileWriter fileWriter = null;
		try {
			manager.start();
			while(manager.readyAgent.get() < 2) {
				Thread.sleep(500);
			}
			fileWriter = new FileWriter(config.getInterlayer_ROOT()+"/log/fuzzresult.log",false);
			fileWriter.write("==================================================================="
					+ "\n#Fuzzing test result:\n");
			for(String stra:stras) {
				String ret = doFuzzing(StrategesGen.getStrategyObject(stra));
				log.info(ret);
				log.info(analyzer.toString());
				log.info("-----------------------------------------------------------------------------");
				fileWriter.write(ret+" ["+stra+"]\n");
				fileWriter.flush();
			}
			//stop 2 agent in VM
			manager.getControllerm().doControl("stop agent");
			manager.getSwitchm().doControl("stop agent");
			manager.running = false;
			manager.interrupt();
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				fileWriter.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	public boolean doOpenFlowFuzzer() {
		String ret = null;
		FileWriter fileWriter =null;
		try {
			manager.start();
			while(manager.readyAgent.get() < 2) {
				Thread.sleep(500);
			}
			fileWriter = new FileWriter(config.getInterlayer_ROOT()+"/log/fuzzresult.log",false);
			fileWriter.write("==================================================================="
					+ "\n#Fuzzing test result:\n");
			ArrayList<String> strategies = StrategesGen.readFromFile(config.getInterlayer_ROOT()+"/pyscript/strategies.txt");
			for(String s:strategies) {
				Strategy strategy = StrategesGen.getStrategyObject(s);
				if(strategy == null) {
					log.info("get strategy failed. strategy: "+s);
					continue;
				}
				if((ret = doFuzzing(strategy)) != null) {
					log.info(ret);
					log.info(analyzer.toString());
					log.info("-----------------------------------------------------------------------------");
					fileWriter.write(ret+" ["+s+"]\n");
					fileWriter.flush();
				}
			}
			//stop 2 agent in VM
			manager.getControllerm().doControl("stop agent");
			manager.getSwitchm().doControl("stop agent");
			manager.running = false;
			manager.interrupt();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				fileWriter.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return true;
	}
	
	public String doFuzzing(Strategy strategy) {
    	String ret = "UNKOWN";
    	try {
    		if(strategy.testWhich.equals("controller")) {
    			log.info("begin to fuzzing test controller, strategy = " + strategy.toString());
    			runController();
    			log.info("waitting controller start...");
    			String line = manager.getControllerm().getIn().readLine();
    			if(line.contains("start")) {
    				ofSwitch = new DummySwitch(strategy);
    				analyzer.init();
    				synchronized (ofSwitch) {
    					ofSwitch.start();
    					ofSwitch.wait(10000);//10sec
					}
    				if(ofSwitch.strategySend) {
						//result analysis
    					Thread.sleep(3000);//this line could be delete if added waiting response notify for other msg
    					analyzer.isSocketClose = ofSwitch.socket.isClosed();
    					ret = analyzer.getResult();
					}else {
						log.info("Test over 10 seconds, test timed out");
					}
    				ofSwitch.running = false;
    				ofSwitch.interrupt();
    				stopController();
    				Thread.sleep(500);
    			}else {
    				log.info("controller start failed.");
    			}
    		}else {//test switch
    			log.info("begin to fuzzing test switch, strategy = " + strategy.toString());
    			analyzer.init();
    			ofController = new DummyController(strategy);
    			manager.getSwitchm().doControl("run mininet");
    			ofController.listeningSwitch();
				synchronized (ofController) {
					ofController.start();
					ofController.wait(10000);//10sec
				}
				if(ofController.strategySend) {
					//result analysis
					Thread.sleep(3000);//this line could be delete if added waiting response notify for other msg
					analyzer.isSocketClose = ofController.serverSock.isClosed();
					ret = analyzer.getResult();
				}else {
					log.info("Test over 10 seconds, test timed out");
				}
				manager.getSwitchm().doControl("stop mininet");
				ofController.running = false;
				ofController.interrupt();
				while(!ofController.serverSock.isClosed()) {
					Thread.sleep(10);
				}
				Thread.sleep(500);
    		}
		} catch (Exception e) {
			manager.getSwitchm().doControl("stop mininet");
			stopController();
			e.printStackTrace();
		}
    	return ret;
    }
    
    public void runController() {
    	switch (config.getTargetController().toLowerCase()) {
    	case "ryu":
    		manager.getControllerm().doControl("run ryu");
    		break;
    	case "floodlight":
    		manager.getControllerm().doControl("run floodlight");
    		break;
    	case "odl":
    		manager.getControllerm().doControl("run odl");
    		break;
    	case "onos":
    		manager.getControllerm().doControl("run onos");
    		break;
    	case "pox":
    		manager.getControllerm().doControl("run pox");
    		break;
    	}
    }
    
    public void stopController() {
    	switch (config.getTargetController().toLowerCase()) {
			case "ryu":
				manager.getControllerm().doControl("stop ryu");
				break;
			case "floodlight":
				manager.getControllerm().doControl("stop floodlight");
				break;
			case "odl":
				manager.getControllerm().doControl("stop odl");
				break;
			case "onos":
				manager.getControllerm().doControl("stop onos");
				break;
			case "pox":
				manager.getControllerm().doControl("stop pox");
				break;
		}
    }
	
}
